package spirit.io.nio;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;

/**
 * @author eric - 好好学习，天天向上！
 * @date 2021/12/27
 * @description NIO SERVER
 */
public class NioServer {

    private int port = 8080;

    // NIO 几个角色
    // 1. 轮询器 Selector （叫号大厅）
    private Selector selector;
    // 2. 缓冲区 buffer （等候区）
    private ByteBuffer buffer = ByteBuffer.allocate(1024);

    /**
     * 初始化
     *
     * @param port 监听的端口
     */
    public NioServer(int port) {
        this.port = port;
        // 初始化 selector 开门营业
        try {
            ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
            // ip + port
            serverSocketChannel.bind(new InetSocketAddress(port));
            // 为了兼容BIO，NIO 默认是阻塞模式，也可以设置非阻塞
            serverSocketChannel.configureBlocking(false);

            selector = Selector.open();
            // 将select 注册进 server， 并声明可以接受请求了。
            serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }


    public void listen() {
        System.out.println("listening in " + this.port);
        // 轮询
        while (true) {
            try {
                // 如果有请求进来的话，先存储进selector， 通过select() 来获取获取数据。
                // 这一步相当于获取一个数据到jvm内存中，后续通过selectedKeys() 来获取。
                selector.select();
                Set<SelectionKey> keys = selector.selectedKeys();
                // 不断的迭代
                Iterator<SelectionKey> iterator = keys.iterator();
                while (iterator.hasNext()) {
                    // 同步体现在这里，因为每次只能处理一个key， 每次只能处理一个状态
                    SelectionKey key = iterator.next();
                    iterator.remove();
                    // 每一个key 获是一种状态
                    process(key);
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 处理key
     *
     * @param key selection key.
     */
    private void process(SelectionKey key) {
        // 针对于每一种状态给一个响应
        if (key.isAcceptable()) {
            try {
                // 可以接受数据了
                ServerSocketChannel serverSocketChannel = (ServerSocketChannel) key.channel();
                SocketChannel socketChannel = serverSocketChannel.accept();
                // 设置为非阻塞
                socketChannel.configureBlocking(false);
                // 当数据准备就绪的时候，将状态改为 可读
                key = socketChannel.register(selector, SelectionKey.OP_READ);
            } catch (IOException e) {
                e.printStackTrace();
            }
        } else if (key.isReadable()) {
            try {
                // 从多路复用器 拿到客户端的引用
                SocketChannel socketChannel = (SocketChannel) key.channel();
                int len = socketChannel.read(buffer);
                if (len > 0) {
                    buffer.flip();
                    String content = new String(buffer.array(), 0, len);
                    key = socketChannel.register(selector, SelectionKey.OP_WRITE);
                    // 在key上携带一个数据，一会再写出去
                    key.attach(content);
                    System.out.println("读取内容：" + content);
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        } else if (key.isWritable()) {
            SocketChannel channel = (SocketChannel) key.channel();
            String content = (String) key.attachment();
            try {
                channel.write(buffer.wrap(("输出" + content).getBytes()));
                channel.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }


    public static void main(String[] args) {
        new NioServer(8080).listen();
    }

}
